"""PPG处理器数据类型定义

定义PPG信号处理过程中使用的所有数据结构，包括：
- 输入信号格式
- 处理结果格式
- HRV分析结果
- 信号质量评估结果

作者: PPG算法包开发团队
版本: 2.0.0
"""

from dataclasses import dataclass, field
from typing import Dict, List, Optional, Any, Union
import numpy as np
import warnings


@dataclass
class PPGSignal:
    """PPG信号数据结构
    
    属性:
        data: PPG信号数据数组
        sampling_rate: 采样率 (Hz)
        timestamps: 时间戳数组（可选，单位：秒）
        metadata: 信号元数据（可选）
    """
    data: np.ndarray
    sampling_rate: float = 25.0
    timestamps: Optional[np.ndarray] = None
    metadata: Optional[Dict[str, Any]] = field(default_factory=dict)
    
    def __post_init__(self):
        """数据验证"""
        if not isinstance(self.data, np.ndarray):
            self.data = np.array(self.data)
        
        if len(self.data) == 0:
            raise ValueError("PPG信号数据不能为空")
        
        if self.sampling_rate <= 0:
            raise ValueError("采样率必须大于0")
        
        # 验证时间戳
        if self.timestamps is not None:
            if not isinstance(self.timestamps, np.ndarray):
                self.timestamps = np.array(self.timestamps)
            
            if len(self.timestamps) != len(self.data):
                raise ValueError(f"时间戳长度({len(self.timestamps)})与数据长度({len(self.data)})不匹配")
            
            # 检查时间戳是否单调递增
            if not np.all(np.diff(self.timestamps) > 0):
                warnings.warn("时间戳不是严格单调递增的，可能存在数据问题")
    
    @property
    def duration(self) -> float:
        """信号时长（秒）
        
        如果有时间戳，使用时间戳计算实际时长；
        否则使用采样率估算时长
        """
        if self.timestamps is not None and len(self.timestamps) > 1:
            # 基于时间戳的实际时长
            return float(self.timestamps[-1] - self.timestamps[0])
        else:
            # 基于采样率的估算时长
            return len(self.data) / self.sampling_rate
    
    @property
    def theoretical_duration(self) -> float:
        """理论时长（基于采样率计算）"""
        return len(self.data) / self.sampling_rate
    
    @property
    def length(self) -> int:
        """信号长度（采样点数）"""
        return len(self.data)
    
    @property
    def has_timestamps(self) -> bool:
        """是否包含时间戳"""
        return self.timestamps is not None
    
    def detect_packet_loss(self, tolerance: float = 0.1) -> Dict[str, Any]:
        """检测丢包情况
        
        参数:
            tolerance: 时间间隔容差（相对于理论间隔的比例）
            
        返回:
            Dict: 丢包检测结果
        """
        if not self.has_timestamps:
            return {
                'has_packet_loss': False,
                'packet_loss_detected': False,
                'message': '无时间戳信息，无法检测丢包'
            }
        
        if len(self.timestamps) < 2:
            return {
                'has_packet_loss': False,
                'packet_loss_detected': False,
                'message': '时间戳数据不足，无法检测丢包'
            }
        
        # 计算理论时间间隔
        theoretical_interval = 1.0 / self.sampling_rate
        
        # 计算实际时间间隔
        actual_intervals = np.diff(self.timestamps)
        
        # 检测异常间隔（超出容差范围）
        expected_min = theoretical_interval * (1 - tolerance)
        expected_max = theoretical_interval * (1 + tolerance)
        
        # 找出异常间隔
        abnormal_intervals = (actual_intervals < expected_min) | (actual_intervals > expected_max)
        packet_loss_indices = np.where(abnormal_intervals)[0]
        
        # 统计丢包信息
        total_gaps = len(packet_loss_indices)
        large_gaps = np.sum(actual_intervals > expected_max * 2)  # 明显的丢包
        
        # 计算丢包率
        if len(actual_intervals) > 0:
            packet_loss_ratio = total_gaps / len(actual_intervals)
        else:
            packet_loss_ratio = 0.0
        
        # 计算时间覆盖率
        actual_duration = self.duration
        theoretical_duration = self.theoretical_duration
        time_coverage = actual_duration / theoretical_duration if theoretical_duration > 0 else 1.0
        
        return {
            'has_packet_loss': total_gaps > 0,
            'packet_loss_detected': total_gaps > 0,
            'total_gaps': int(total_gaps),
            'large_gaps': int(large_gaps),
            'packet_loss_ratio': float(packet_loss_ratio),
            'time_coverage': float(time_coverage),
            'actual_duration': float(actual_duration),
            'theoretical_duration': float(theoretical_duration),
            'gap_indices': packet_loss_indices.tolist(),
            'mean_interval': float(np.mean(actual_intervals)),
            'std_interval': float(np.std(actual_intervals)),
            'theoretical_interval': float(theoretical_interval),
            'message': f'检测到{total_gaps}个时间间隔异常，其中{large_gaps}个明显丢包'
        }
    
    def get_time_axis(self) -> np.ndarray:
        """获取时间轴
        
        返回:
            np.ndarray: 时间轴数组（秒）
        """
        if self.has_timestamps:
            return self.timestamps.copy()
        else:
            # 生成基于采样率的时间轴
            return np.arange(len(self.data)) / self.sampling_rate
    
    def interpolate_missing_data(self, method: str = 'linear') -> 'PPGSignal':
        """插值填补丢失的数据点
        
        参数:
            method: 插值方法 ('linear', 'cubic', 'nearest')
            
        返回:
            PPGSignal: 插值后的信号
        """
        if not self.has_timestamps:
            warnings.warn("无时间戳信息，无法进行插值")
            return self
        
        packet_loss_info = self.detect_packet_loss()
        if not packet_loss_info['has_packet_loss']:
            return self  # 无丢包，直接返回
        
        try:
            from scipy import interpolate
        except ImportError:
            warnings.warn("需要安装scipy库才能使用插值功能")
            return self
        
        # 生成理想的时间轴
        start_time = self.timestamps[0]
        end_time = self.timestamps[-1]
        theoretical_points = int((end_time - start_time) * self.sampling_rate) + 1
        ideal_timestamps = np.linspace(start_time, end_time, theoretical_points)
        
        # 插值数据
        if method == 'linear':
            f = interpolate.interp1d(self.timestamps, self.data, kind='linear', 
                                   bounds_error=False, fill_value='extrapolate')
        elif method == 'cubic':
            f = interpolate.interp1d(self.timestamps, self.data, kind='cubic',
                                   bounds_error=False, fill_value='extrapolate')
        elif method == 'nearest':
            f = interpolate.interp1d(self.timestamps, self.data, kind='nearest',
                                   bounds_error=False, fill_value='extrapolate')
        else:
            raise ValueError(f"不支持的插值方法: {method}")
        
        interpolated_data = f(ideal_timestamps)
        
        # 创建新的PPGSignal对象
        new_metadata = self.metadata.copy()
        new_metadata['interpolated'] = True
        new_metadata['interpolation_method'] = method
        new_metadata['original_length'] = len(self.data)
        new_metadata['interpolated_length'] = len(interpolated_data)
        
        return PPGSignal(
            data=interpolated_data,
            sampling_rate=self.sampling_rate,
            timestamps=ideal_timestamps,
            metadata=new_metadata
        )


@dataclass
class HRVResults:
    """心率变异性(HRV)分析结果
    
    时域指标:
        mean_nn: 平均RR间隔 (ms)
        sdnn: RR间隔标准差 (ms)
        rmssd: 相邻RR间隔差值的均方根 (ms)
        pnn50: 相邻RR间隔差值>50ms的百分比 (%)
        pnn20: 相邻RR间隔差值>20ms的百分比 (%)
        triangular_index: 三角指数
    
    频域指标:
        vlf_power: 极低频功率 (ms²)
        lf_power: 低频功率 (ms²)
        hf_power: 高频功率 (ms²)
        total_power: 总功率 (ms²)
        lf_nu: 低频标准化单位
        hf_nu: 高频标准化单位
        lf_hf_ratio: LF/HF比值
    """
    # 时域指标
    mean_nn: float = 0.0
    sdnn: float = 0.0
    rmssd: float = 0.0
    pnn50: float = 0.0
    pnn20: float = 0.0
    triangular_index: float = 0.0
    
    # 频域指标
    vlf_power: float = 0.0
    lf_power: float = 0.0
    hf_power: float = 0.0
    total_power: float = 0.0
    lf_nu: float = 0.0
    hf_nu: float = 0.0
    lf_hf_ratio: float = 0.0
    
    # 原始数据
    rr_intervals: Optional[np.ndarray] = None
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典格式"""
        return {
            'time_domain': {
                'mean_nn': self.mean_nn,
                'sdnn': self.sdnn,
                'rmssd': self.rmssd,
                'pnn50': self.pnn50,
                'pnn20': self.pnn20,
                'triangular_index': self.triangular_index
            },
            'frequency_domain': {
                'vlf_power': self.vlf_power,
                'lf_power': self.lf_power,
                'hf_power': self.hf_power,
                'total_power': self.total_power,
                'lf_nu': self.lf_nu,
                'hf_nu': self.hf_nu,
                'lf_hf_ratio': self.lf_hf_ratio
            }
        }


@dataclass
class QualityResults:
    """信号质量评估结果
    
    属性:
        snr: 信噪比 (dB)
        peak_freq: 主频率 (Hz)
        peak_hr: 主频率对应心率 (BPM)
        hr_power_ratio: 心率频段功率比 (%)
        quality_score: 综合质量评分 (0-100)
        quality_segments: 高质量信号段 [(start, end), ...]
        quality_grade: 质量等级字符串
        peak_regularity: 峰值规律性 (0-1)
        artifacts_ratio: 伪影比例 (0-1)
    """
    snr: float = 0.0
    peak_freq: float = 0.0
    peak_hr: float = 0.0
    hr_power_ratio: float = 0.0
    quality_score: float = 0.0
    quality_segments: List[tuple] = field(default_factory=list)
    quality_grade: str = "unknown"
    peak_regularity: float = 0.0
    artifacts_ratio: float = 0.0
    
    @property
    def quality_level(self) -> str:
        """质量等级"""
        if self.quality_score >= 80:
            return "优秀"
        elif self.quality_score >= 60:
            return "良好"
        elif self.quality_score >= 40:
            return "一般"
        else:
            return "差"


@dataclass
class PPGResults:
    """PPG处理完整结果
    
    属性:
        original_signal: 原始信号
        processed_signal: 处理后信号
        peaks: 检测到的峰值位置
        heart_rates: 瞬时心率序列 (BPM)
        mean_heart_rate: 平均心率 (BPM)
        hrv_results: HRV分析结果
        quality_results: 信号质量评估结果
        processing_info: 处理过程信息
        pipeline_name: 使用的处理管道名称
    """
    original_signal: np.ndarray
    processed_signal: np.ndarray
    peaks: np.ndarray
    heart_rates: Optional[np.ndarray] = None
    mean_heart_rate: float = 0.0
    hrv_results: Optional[HRVResults] = None
    quality_results: Optional[QualityResults] = None
    processing_info: Dict[str, Any] = field(default_factory=dict)
    pipeline_name: str = ""
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典格式，便于序列化和API返回"""
        result = {
            'peaks_count': len(self.peaks),
            'mean_heart_rate': self.mean_heart_rate,
            'pipeline_name': self.pipeline_name,
            'processing_info': self.processing_info
        }
        
        if self.heart_rates is not None:
            result['heart_rate_stats'] = {
                'mean': float(np.mean(self.heart_rates)),
                'std': float(np.std(self.heart_rates)),
                'min': float(np.min(self.heart_rates)),
                'max': float(np.max(self.heart_rates))
            }
        
        if self.hrv_results:
            result['hrv_results'] = self.hrv_results.to_dict()
        
        if self.quality_results:
            result['quality_results'] = {
                'snr': self.quality_results.snr,
                'peak_freq': self.quality_results.peak_freq,
                'peak_hr': self.quality_results.peak_hr,
                'hr_power_ratio': self.quality_results.hr_power_ratio,
                'quality_score': self.quality_results.quality_score,
                'quality_level': self.quality_results.quality_level,
                'quality_segments_count': len(self.quality_results.quality_segments)
            }
        
        return result
    
    def summary(self) -> str:
        """生成处理结果摘要"""
        summary_lines = [
            f"PPG处理结果摘要 (管道: {self.pipeline_name})",
            f"=" * 50,
            f"检测峰值数: {len(self.peaks)}",
            f"平均心率: {self.mean_heart_rate:.1f} BPM"
        ]
        
        if self.heart_rates is not None:
            summary_lines.extend([
                f"心率范围: {np.min(self.heart_rates):.1f} - {np.max(self.heart_rates):.1f} BPM",
                f"心率标准差: {np.std(self.heart_rates):.1f} BPM"
            ])
        
        if self.quality_results:
            summary_lines.extend([
                f"信号质量: {self.quality_results.quality_level} ({self.quality_results.quality_score:.0f}/100)",
                f"信噪比: {self.quality_results.snr:.1f} dB"
            ])
        
        if self.hrv_results:
            summary_lines.extend([
                f"HRV - SDNN: {self.hrv_results.sdnn:.1f} ms",
                f"HRV - RMSSD: {self.hrv_results.rmssd:.1f} ms",
                f"HRV - pNN50: {self.hrv_results.pnn50:.1f}%"
            ])
        
        return "\n".join(summary_lines)


# 类型别名
PPGData = Union[np.ndarray, List[float], PPGSignal]
ProcessingConfig = Dict[str, Any]